package com.ruoyi.system.filter;

import cn.hutool.core.collection.CollUtil;
import com.ruoyi.common.core.enums.DataPermission;
import com.ruoyi.common.domain.BaseEntityProps;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.system.api.model.LoginUser;
import com.ruoyi.system.domain.*;
import com.ruoyi.system.domain.dto.SysUserCacheDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.babyfish.jimmer.meta.ImmutableType;
import org.babyfish.jimmer.sql.JSqlClient;
import org.babyfish.jimmer.sql.ast.LikeMode;
import org.babyfish.jimmer.sql.ast.Predicate;
import org.babyfish.jimmer.sql.ast.PropExpression;
import org.babyfish.jimmer.sql.filter.Filter;
import org.babyfish.jimmer.sql.filter.FilterArgs;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

@RequiredArgsConstructor
@Component
@Slf4j
public class AuthDataFilter implements Filter<BaseEntityProps> {

    private final JSqlClient sqlClient;

    @Override
    public void filter(FilterArgs<BaseEntityProps> args) {
        BaseEntityProps table = args.getTable();
        ImmutableType immutableType = table.getImmutableType();
        if (SysRoleTable.$.getImmutableType().equals(immutableType)) {
            return;
        }
        LoginUser loginUser = SecurityUtils.getLoginUser();
        List<SysUserCacheDTO.TargetOf_roles> roles = loginUser.getSysUser().getRoles();
        if (CollUtil.isEmpty(roles)) {
            return;
        }
        List<Long> deptIdList = new ArrayList<>();
        if (SecurityUtils.isAdmin(loginUser.getUserid()) || roles.stream().anyMatch(a -> a.getDataScope().equals(DataPermission.ALL.getValue()))) {
            return;
        } else if (roles.stream().anyMatch(a -> a.getDataScope().equals(DataPermission.DEPT_AND_CHILD.getValue()))) {
            Long deptId = loginUser.getDeptId();
            deptIdList.add(deptId);
            SysUserCacheDTO.TargetOf_dept dept = loginUser.getSysUser().getDept();
            SysDeptTable deptTable = SysDeptTable.$;
            List<SysDept> sysDepts = sqlClient.filters(it -> it.disableByTypes(AuthDataFilter.class))
                    .createQuery(deptTable)
                    .where(deptTable.ancestors().likeIf(dept.getAncestors() + "," + deptId, LikeMode.START))
                    .select(deptTable.fetch(
                            SysDeptFetcher.$.allScalarFields()
                    )).execute();
            deptIdList.addAll(sysDepts.stream().map(SysDept::deptId).toList());
        } else if (roles.stream().anyMatch(a -> a.getDataScope().equals(DataPermission.DEPT.getValue()))) {
            Long deptId = loginUser.getDeptId();
            deptIdList.add(deptId);
        } else if (roles.stream().anyMatch(a -> a.getDataScope().equals(DataPermission.CUSTOM.getValue()))) {
            SysDeptTable dt = SysDeptTable.$;
            List<SysUserCacheDTO.TargetOf_roles> list = roles.stream().filter(a -> a.getDataScope().equals(DataPermission.CUSTOM.getValue())).toList();
            // 获取自定义角色关联的所有部门
            List<SysDept> sysDepts = sqlClient.filters(it -> it.disableByTypes(AuthDataFilter.class))
                    .createQuery(dt)
                    .where(dt.asTableEx().roles().roleId().in(list.stream().map(SysUserCacheDTO.TargetOf_roles::getRoleId).toList()))
                    .select(dt.fetch(SysDeptFetcher.$.allScalarFields()))
                    .execute();
            deptIdList.addAll(sysDepts.stream().map(SysDept::deptId).toList());
        } else if (roles.stream().anyMatch(a -> a.getDataScope().equals(DataPermission.PERSONAL.getValue()))) {
            PropExpression<SysUser> createBy = table.get("createBy");
            args.where(((SysUserTable)createBy).asTableEx().userId().eq(loginUser.getUserid()));
        }
        if (CollUtil.isNotEmpty(deptIdList)) {
            SysDeptTable deptTable = SysDeptTable.$;
            SysUserTable userTable = SysUserTable.$;
            if (deptTable.getImmutableType().equals(immutableType)) {
                if (roles.stream().anyMatch(a -> a.getDataScope().equals(DataPermission.PERSONAL.getValue()))) {
                    args.where(
                            Predicate.or(
                                    deptTable.createBy().asTableEx().userId().eq(loginUser.getUserid()),
                                    deptTable.get("deptId").in(new ArrayList<>(deptIdList))
                            ));
                } else {
                    args.where(table.get("deptId").in(new ArrayList<>(deptIdList)));
                }
            } else if (userTable.getImmutableType().equals(immutableType)) {
                if (roles.stream().anyMatch(a -> a.getDataScope().equals(DataPermission.PERSONAL.getValue()))) {
                    args.where(
                            Predicate.or(
                                    userTable.userId().eq(loginUser.getUserid()),
                                    userTable.deptId().in(deptIdList)
                            ));
                } else {
                    args.where(userTable.deptId().in(deptIdList));
                }
            } else {
                if (roles.stream().anyMatch(a -> a.getDataScope().equals(DataPermission.PERSONAL.getValue()))) {
                    PropExpression<SysUser> createBy = table.get("createBy");
                    args.where(
                            Predicate.or(
                                    ((SysUserTable)createBy).asTableEx().userId().eq(loginUser.getUserid()),
                                    ((SysUserTable)createBy).asTableEx().deptId().in(deptIdList)
                            ));
                } else {
                    PropExpression<SysUser> createBy = table.get("createBy");
                    args.where(((SysUserTable)createBy).createBy().asTableEx().deptId().in(deptIdList));
                }
            }

        }
    }
}
